﻿using System;
using System.Data;
using System.Data.Common;

namespace Sofire.Data
{
    /// <summary>
    /// 查询引擎的上下文对象，一个对象创建一个数据库连接。
    /// </summary>
    public partial class QueryContext : IDisposable
    {
        private readonly QueryEngineBase Engine;
        private readonly DbConnection Connection;
        private DbTransaction Transaction;
        /// <summary>
        /// 获取连接上下文。
        /// </summary>
        public DbConnection ContextConnection
        {
            get
            {
                return this.Connection;
            }
        }
        /// <summary>
        /// 获取事务上下文。
        /// </summary>
        public DbTransaction ContextTransaction
        {
            get
            {
                return this.Transaction;
            }
        }

        internal QueryContext(QueryEngineBase engine)
        {
            if(engine == null) throw new ArgumentNullException("engine");
            this.Engine = engine;
            this.Connection = engine.CreateConnection();
        }

        private void BulkCopyMSSQL(BulkCopyArguments args)
        {
            System.Data.SqlClient.SqlBulkCopy bulkCopy = new System.Data.SqlClient.SqlBulkCopy(this.Connection as System.Data.SqlClient.SqlConnection);
            bulkCopy.BatchSize = args.BatchSize;
            bulkCopy.BulkCopyTimeout = args.BulkCopyTimeout;
            bulkCopy.DestinationTableName = args.DestinationTableName;
            bulkCopy.NotifyAfter = args.NotifyAfter;
            if(args.RowsCopied != null)
            {
                bulkCopy.SqlRowsCopied += (ss, ee) =>
                {
                    ee.Abort = args.RowsCopied(ee.RowsCopied);
                };
            }
            bulkCopy.WriteToServer(args.Table, args.RowState);
        }

        private void BulkCopyOracle(BulkCopyArguments args)
        {
            DDTek.Oracle.OracleBulkCopy bulkCopy = new DDTek.Oracle.OracleBulkCopy(this.Connection as DDTek.Oracle.OracleConnection);
            bulkCopy.BatchSize = args.BatchSize;
            bulkCopy.BulkCopyTimeout = args.BulkCopyTimeout;
            bulkCopy.DestinationTableName = args.DestinationTableName;
            bulkCopy.NotifyAfter = args.NotifyAfter;
            if(args.RowsCopied != null)
            {
                bulkCopy.OracleRowsCopied += (ss, ee) =>
                {
                    ee.Abort = args.RowsCopied(ee.RowsCopied);
                };
            }
            bulkCopy.WriteToServer(args.Table, args.RowState);
        }

        /// <summary>
        /// 批量数据导入。
        /// </summary>
        /// <param name="args">参数。</param>
        public void BulkCopy(BulkCopyArguments args)
        {
            switch(this.Engine.Provider)
            {
                case QueryEngineProvider.Microsoft_SQL_Server:
                    this.BulkCopyMSSQL(args);
                    break;
                case QueryEngineProvider.Oracle:
                    this.BulkCopyOracle(args);
                    break;
                default:
                    throw new NotSupportedException("当前数据提供程序不支持批量加载功能。");
            }
        }

        /// <summary>
        /// 获取一个值，指示当前上下文的连接是否已关闭。
        /// </summary>
        public bool IsClosed
        {
            get
            {
                return this.Connection.State == ConnectionState.Closed;
            }
        }

        /// <summary>
        /// 打开连接。在执行查询时，若数据库尚未打开则自动打开数据库。
        /// </summary>
        public void Open()
        {
            if(this.Connection.State == ConnectionState.Closed) this.Connection.Open();
        }

        /// <summary>
        /// 启动数据库事务，并打开数据库连接。
        /// </summary>
        public void OpenTransaction()
        {
            this.OpenTransaction(IsolationLevel.Unspecified);
        }

        /// <summary>
        /// 以指定的隔离级别启动数据库事务，并打开数据库连接。
        /// </summary>
        /// <param name="isolationLevel">指定事务的隔离级别。</param>
        public void OpenTransaction(IsolationLevel isolationLevel)
        {
            if(this.Connection.State != ConnectionState.Closed) throw new NotSupportedException("连接已打开！无法打开事务");
            this.Connection.Open();
            this.Transaction = Connection.BeginTransaction();
        }

        /// <summary>
        /// 提交数据库事务。
        /// </summary>
        /// <returns>返回操作的结果。</returns>
        public Result Commit()
        {
            if(this.Transaction == null) throw new NotSupportedException("并非以事务的方式打开连接。");
            try
            {
                this.Transaction.Commit();
            }
            catch(Exception ex)
            {
                return ex;
            }
            return Result.Successfully;
        }

        /// <summary>
        /// 从挂起状态回滚事务。
        /// </summary>
        /// <returns>返回操作的结果。</returns>
        public Result Rollback()
        {
            if(this.Transaction == null) throw new NotSupportedException("并非以事务的方式打开连接。");
            try
            {
                this.Transaction.Rollback();
            }
            catch(Exception ex)
            {
                return ex;
            }
            return Result.Successfully;
        }

        /// <summary>
        /// 关闭连接。当前上下文没有任何引用关联时，将会自动关闭连接，但不推荐这种方式。
        /// </summary>
        public void Close()
        {
            if(this.Connection.State != ConnectionState.Closed)
            {
                try
                {
                    if(this.Transaction != null) this.Transaction.Dispose();
                }
                catch(Exception) { }
                try
                {
                    this.Connection.Close();
                }
                catch(Exception) { }
            }
        }

        #region IDisposable 成员

        void IDisposable.Dispose()
        {
            this.Close();
        }

        #endregion

        /// <summary>
        /// 析构函数。
        /// </summary>
        ~QueryContext()
        {
            this.Close();
        }
    }
}
